#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "generate.h"

int dynamics (int N, int state[N], int network[N][N])
{
	int j, k, p, flag;
	int state2[N];

	for (j = 0; j < N; j++)
	{
		flag = 0;
		for (k = 0; k < N; k++)
		{
			if (k == j)
				continue;
			if (state[k] == 1 && network[j][k] == -1)
			{
				state2[j] = 0;
				flag = 1;
				break;
			}
		}

		if (flag == 1)
			continue;

		for (k = 0; k < N; k++)
		{
			if (state[k] == 1 && network[j][k] == 1)
			{
				state2[j] = 1;
				flag = 1;
				break;
			}
		}

		if (flag == 1)
			continue;

		if (network[j][j] == -1)
			state2[j] = 0;
		else
			state2[j] = state[j];
	}

	p = state2[0];
	for (j = 1; j < N; j++)
	{
		p = 2 * p + state2[j];
	}

	return p;
}

int check_stable2 (int N, int i, int status1[N], int status2[N], int network[N][N])
{
	int j;

	if (status2[i] == 0)
	{
		/* check stable inhibition */
		for (j = 0; j < N; j++)
		{
			if (i == j)
				continue;

			if (status1[j] == 1 && status2[j] == 1 && network[i][j] == -1)
				return 1;
		}

		/* check self-activation */
		if (network[i][i] == 1)
			return 0;

		/* check activation */
		for (j = 0; j < N; j++)
		{
			if (i == j)
				continue;

			if ((status1[j] == 1 || status2[j] == 1) && network[i][j] == 1)
				return 0;
		}

		if (status1[i] == 0)
			return 1;
		else
			return 0;
	}
	else
	{
		/* check inhibition */
		for (j = 0; j < N; j++)
		{
			if (i == j)
				continue;

			if (status2[j] == 1 && network[i][j] == -1)
				return 0;
		}

		if (status1[i] == 1 && network[i][i] != -1)
			return 1;

		/* check self-activation */
		if (network[i][i] == 1)
			return 1;

		/* check stable activation */
		for (j = 0; j < N; j++)
		{
			if (i == j)
				continue;

			if (status1[j] == 1 && status2[j] == 1 && network[i][j] == 1)
				return 1;
		}

		return 0;
	}
}

int check_stable (int N, int i, struct state *state, int network[N][N], int mode)
{
	if (mode == 0)
		return 1;

	int *status1, *status2, *status3;

	status1 = state[i].status;
	status2 = state[state[i].status2].status;
	status3 = state[state[state[i].status2].status2].status;

	int j, flag, flag1, flag2;

	flag = 1;

	if (mode == 1 || mode == 3)
		flag1 = 1;
	else
		flag1 = 0;

	if (mode == 2 || mode == 3)
		flag2 = 1;
	else
		flag2 = 0;

	for (j = 0; j < N; j++)
	{
		if ((flag1 == 1 && status1[j] != status2[j]) || (flag2 == 1 && status2[j] == status3[j]))
		{
			flag = check_stable2 (N, j, status1, status2, network);
			if (flag == 0)
				break;
		}
	}

	return flag;
}

int find_final (int N, int i, struct state *state, int m)
{
	int n, r;

	n = state[i].status2;

	state[i].length = m;

	if (state[n].flag == -1)
	{
		state[i].flag = -2;
		r = state[i].final = find_final (N, n, state, m + 1);
		if (state[n].flag == -3)
		{
			state[n].flag = 3;
			state[n].final = n;
			state[n].length = state[state[n].status2].length;
			state[i].flag = 2;
			state[i].final = n;
			state[i].length = 1;
		}
		else if (state[i].flag != -3 || m == 0)
		{
			state[i].flag = state[n].flag;
			if (state[n].flag <= 2)
				state[i].length = state[n].length + 1;
			else if (state[n].flag == 3)
			{
				state[i].length = state[n].length;
				state[i].final = i;
			}
		}
	}
	else if (state[n].flag == 0 || state[n].flag == 1)
	{
		state[i].flag = 0;
		r = state[i].final = state[n].final;
		state[i].length = state[n].length + 1;
	}
	else if (state[n].flag == 2)
	{
		state[i].flag = 2;
		r = state[i].final = state[n].final;
		state[i].length = state[n].length + 1;
	}
	else if (state[n].flag == 3)
	{
		state[i].flag = 2;
		r = state[i].final = state[n].final;
		state[i].length = 1;
	}
	else if (state[n].flag == 4 || state[n].flag == 5)
	{
		state[i].flag = 4;
		r = state[i].final = state[n].final;
		state[i].length = state[n].length + 1;
	}
	else if (state[n].flag == -2)
	{
		state[n].flag = -3;
		state[i].flag = 3;
		state[i].length = m - state[n].length + 1;
		r = state[i].final = i;
	}

	return r;
}

void check_final (int N, int n, struct state *state)
{
	int j;

	for (j = 0; j < n; j++)
	{
		if (state[j].stable == 0)
		{
			state[j].flag = 4;
			state[j].final = j;
			state[j].length = 0;
		}
		else if (state[j].status1 == state[j].status2)
		{
			state[j].flag = 1;
			state[j].final = j;
			state[j].length = 1;
		}
		else
		{
			state[j].flag = -1;
			state[j].final = -1;
			state[j].length = -1;
		}
	}

	for (j = 0; j < n; j++)
	{
		if (state[j].final == -1)
		{
			find_final (N, j, state, 0);
		}
	}
}

void get_process (int N, int L, int pathway[L][N], struct state *state, int start)
{
	int i, p;

	p = start;
	for (i = 0; i < L; i++)
	{
		memcpy (pathway[i], state[p].status, sizeof (int) * N);
		state[p].pnflag = 1;
		p = state[p].status2;
	}
}

int check_trigglecycle (int N, int L, int pathway[L][N])
{
	int i, p;

	p = -1;
	for (i = 0; i < N; i++)
	{
		if (pathway[0][i] != pathway[L - 1][i])
		{
			if (p == -1)
				p = i;
			else
				return -1;
		}
	}

	if (pathway[0][p] == 0)
		return -1;

	for (i = 1; i < L - 1; i++)
	{
		if (pathway[i][p] == 1)
			return -1;
	}

	return p;
}

int check_process (int N, int L, int pathway[L][N])
{
	int i, j, flag;

	for (i = 0; i < N; i++)
	{
		flag = 0;
		for (j = 1; j < L; j++)
		{
			if (pathway[j][i] != pathway[j - 1][i])
			{
				flag = 1;
				break;
			}
		}

		if (flag == 0)
			break;
	}

	return flag;
}

void print_process (int N, int L, int pathway[L][N], int mode, int trigger, int no)
{
	int i, j;

	if (mode == 0)
		printf ("c candidate process %d\n", no);
	else if (mode == 1)
		printf ("c candidate cycled process %d\n", no);
	else if (mode == 2)
		printf ("c candidate triggled cycle process %d\n", no);

	for (i = 0; i < L; i++)
	{
		if (mode == 2)
		{
			printf ("%d ", pathway[i][trigger]);
		}

		for (j = 0; j < N; j++)
		{
			if (mode != 2 || j != trigger)
			printf ("%d ", pathway[i][j]);
		}
		printf ("\n");
	}
}

void generate_network2 (int N, int mode1, int network[N][N], float ratio, float ratio2, int *nR, int *nG)
{
	int j, k, x, y;

	for (j = 0; j < N; j++)
	{
		for (k = 0; k < N; k++)
		{
			network[j][k] = -2;
		}
	}

	for (j = 0; j < N; )
	{
		x = drand48 () * N;
		y = drand48 () * N;
		if (x != y && (mode1 == 1 || j != x))
		{
			network[j][x] = 1;
			network[j][y] = -1;
			j ++;
		}
	}

	*nR = *nG = N;
	for (j = 0; j < N; j++)
	{
		for (k = 0; k < N; k++)
		{
			if (network[j][k] == -2)
			{
				float tmp;

				tmp = drand48 ();

				if (tmp > ratio)
					network[j][k] = 0;
				else
				{
					if (mode1 == 0 && j == k)
					{
						network[j][k] = -1;
						(*nR) ++;
					}
					else if (tmp < ratio / (ratio2 + 1.0))
					{
						network[j][k] = 1;
						(*nG) ++;
					}
					else
					{
						network[j][k] = -1;
						(*nR) ++;
					}
				}
			}
		}
	}
}

int check_network (int N, int network[N][N])
{
	int i, j, flag;

	for (i = 0; i < N; i++)
	{
		flag = 0;
		for (j = 0; j < N; j++)
		{
			flag += network[j][i] * network[j][i];
		}
		if (flag == 0)
			break;
	}

	return flag;
}

void generate_network (int N, int mode, int mode1, int network[N][N], float ratio, float ratio2, int *nR, int *nG)
{
	while (1)
	{
		generate_network2 (N, mode1, network, ratio, ratio2, nR, nG);
		if (check_network (N, network) != 0)
			break;
	}
}

void setup_state (int N, int n, int state[N])
{
	int i;

	for (i = 0; i < N; i++)
	{
		state[N - 1 - i] = n % 2;
		n = n / 2;
	}
}

void init_state (struct state *state, int n, int N)
{
	int i;

	for (i = 0; i < n; i++)
	{
		state[i].status = calloc (N, sizeof (int));
		setup_state (N, i, state[i].status);
		state[i].status1 = i;
	}
}
